/***************************************************************
 *
 * (c) 2008 Markus Dittrich 
 *
 * This program is free software; you can redistribute it 
 * and/or modify it under the terms of the GNU General Public 
 * License Version 3 as published by the Free Software Foundation. 
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License Version 3 for more details.
 *
 * You should have received a copy of the GNU General Public 
 * License along with this program; if not, write to the Free 
 * Software Foundation, Inc., 59 Temple Place - Suite 330, 
 * Boston, MA 02111-1307, USA.
 *
 ****************************************************************/

/* STL includes */
#include <string>
#include <limits>

/* local includes */
#include "UtilitiesTest.h"

/* NOTE: currently, depending on the optimization it seems to
 * be impossible to match the variance and stddev for double
 * within EPSILON_DOUBLE; hence, we use a more relaxed criterium
 * until this is fixed */
const double RELAXED_EPSILON_DOUBLE = 2e-15;


//----------------------------------------------------------------
// initialization routine for class data
//----------------------------------------------------------------
void UtilitiesTest::setUp()
{
  /* data for test_string_to_double_conversion */
  doubleString1_ = "3.41 2.33 4.12";
  doubleString2_ = "4.32;4.55;-0.32";
  doubleString3_ = "-1& & ";

  /* data for test_strip_path */
  path1_ = "/foo/bar/somefile.txt";
  path2_ = "/another//path_%#$/ with spaced N /good_stuff.bar";
  path3_ = "//super////// /duper$ugly ?/path to/the_top.dat";

  /* data for test_data_max */
  dataArray1_.push_back(-1.20);
  dataArray1_.push_back(0.00);
  dataArray1_.push_back(-1.24);
  dataArray1_.push_back(5.10);

  dataArray2_ = dataArray1_;
  dataArray2_.push_back(MAX_DOUBLE);
  dataArray2_.push_back(-MAX_DOUBLE);

  dataArray3_ = std::vector<double>(100,0.0);

  dataArray4_.push_back(-1);
  dataArray4_.push_back(-5);
  dataArray4_.push_back(100);
  dataArray4_.push_back(33);
  dataArray4_.push_back(12);
}


//----------------------------------------------------------------
// test proper conversion of strings to vector<double>
//----------------------------------------------------------------
void UtilitiesTest::test_string_to_double_conversion()
{
  /* test 1*/
  std::vector<double> test1;
  string_to_doubles(test1,doubleString1_);
  CPPUNIT_ASSERT( fabs(test1[0] - 3.41) < EPSILON_DOUBLE );
  CPPUNIT_ASSERT( fabs(test1[1] - 2.33) < EPSILON_DOUBLE );
  CPPUNIT_ASSERT( fabs(test1[2] - 4.12) < EPSILON_DOUBLE );

  /* test 2*/
  std::vector<double> test2;
  string_to_doubles(test2,doubleString2_,100,Separator(";"));
  CPPUNIT_ASSERT( fabs(test2[0] - 4.32) < EPSILON_DOUBLE );
  CPPUNIT_ASSERT( fabs(test2[1] - 4.55) < EPSILON_DOUBLE );
  CPPUNIT_ASSERT( fabs(test2[2] + 0.32) < EPSILON_DOUBLE );

  /* test 3*/
  std::vector<double> test3;
  string_to_doubles(test3,doubleString3_,100,Separator("&"));
  CPPUNIT_ASSERT( fabs(test3[0] + 1 ) < EPSILON_DOUBLE );
  CPPUNIT_ASSERT( fabs(test3[1] ) < EPSILON_DOUBLE );
  CPPUNIT_ASSERT( fabs(test3[2] ) < EPSILON_DOUBLE );

}


//----------------------------------------------------------------
// test proper stripping of parent paths from file path
//----------------------------------------------------------------
void UtilitiesTest::test_strip_path()
{
  /* test 1 */
  QString file1 = strip_path(path1_);
  CPPUNIT_ASSERT( file1 == "somefile.txt" );

  /* test 2 */
  QString file2 = strip_path(path2_);
  CPPUNIT_ASSERT( file2 == "good_stuff.bar" );

  /* test 3 */
  QString file3 = strip_path(path3_);
  CPPUNIT_ASSERT( file3 == "the_top.dat" );
}


//----------------------------------------------------------------
// test routine checking the max value in a range of 
// data values
//----------------------------------------------------------------
void UtilitiesTest::test_data_max()
{
  /* test 1 */
  double max1;
  data_max(dataArray1_.begin(), dataArray1_.end(), max1);
  CPPUNIT_ASSERT( fabs(max1 - 5.10) < EPSILON_DOUBLE );

  /* test 2 */
  double max2;
  data_max(dataArray2_.begin(), dataArray2_.end(), max2);
  CPPUNIT_ASSERT( fabs(max2 - MAX_DOUBLE) < EPSILON_DOUBLE );

  /* test 3 */
  double max3;
  data_max(dataArray3_.begin(), dataArray3_.end(), max3);
  CPPUNIT_ASSERT( fabs(max3) < EPSILON_DOUBLE );

  /* test 4 */
  double max4;
  data_max(dataArray4_.begin(), dataArray4_.end(), max4);
  CPPUNIT_ASSERT( fabs(max4 - 100) < EPSILON_DOUBLE );
}


//----------------------------------------------------------------
// test routine checking the min value in a range of 
// data values
//----------------------------------------------------------------
void UtilitiesTest::test_data_min()
{
  /* test 1 */
  double min1;
  data_min(dataArray1_.begin(), dataArray1_.end(), min1);
  CPPUNIT_ASSERT( fabs(min1 + 1.24) < EPSILON_DOUBLE );

  /* test 2 */
  double min2;
  data_min(dataArray2_.begin(), dataArray2_.end(), min2);
  CPPUNIT_ASSERT( fabs(min2 + MAX_DOUBLE) < EPSILON_DOUBLE );

  /* test 3 */
  double min3;
  data_min(dataArray3_.begin(), dataArray3_.end(), min3);
  CPPUNIT_ASSERT( fabs(min3) < EPSILON_DOUBLE );

  /* test 4 */
  double min4;
  data_min(dataArray4_.begin(), dataArray4_.end(), min4);
  CPPUNIT_ASSERT( fabs(min4 + 5) < EPSILON_DOUBLE );
}


//----------------------------------------------------------------
// test routine checking the mean value of a range of 
// data values
//----------------------------------------------------------------
void UtilitiesTest::test_data_mean()
{
  /* test 1 */
  double mean1;
  data_mean(dataArray1_.begin(), dataArray1_.end(), mean1);
  CPPUNIT_ASSERT( fabs(mean1 - 0.665 ) < EPSILON_DOUBLE );

  /* test 2 */
  double mean2;
  data_mean(dataArray2_.begin(), dataArray2_.end(), mean2);
  CPPUNIT_ASSERT( fabs(mean2) < EPSILON_DOUBLE );

  /* test 3 */
  double mean3;
  data_mean(dataArray3_.begin(), dataArray3_.end(), mean3);
  CPPUNIT_ASSERT( fabs(mean3) < EPSILON_DOUBLE );

  /* test 4 */
  double mean4;
  data_mean(dataArray4_.begin(), dataArray4_.end(), mean4);
  CPPUNIT_ASSERT( fabs(mean4 - 27.8) < EPSILON_DOUBLE );
}


//----------------------------------------------------------------
// test routine checking the variance of a range of 
// data values
//----------------------------------------------------------------
void UtilitiesTest::test_data_variance()
{
  /* test 1 */
  double variance1;
  data_variance(dataArray1_.begin(), dataArray1_.end(), variance1);
  CPPUNIT_ASSERT( fabs(variance1 - 9.0729 ) < RELAXED_EPSILON_DOUBLE );

  /* test 2 */
  double variance2;
  bool result = data_variance(dataArray2_.begin(), dataArray2_.end(), 
      variance2);
  CPPUNIT_ASSERT( result == false );

  /* test 3 */
  double variance3;
  data_variance(dataArray3_.begin(), dataArray3_.end(), variance3);
  CPPUNIT_ASSERT( fabs(variance3) < EPSILON_DOUBLE );

  /* test 4 */
  double variance4;
  data_variance(dataArray4_.begin(), dataArray4_.end(), variance4);
  CPPUNIT_ASSERT( fabs(variance4 - 1848.7) < EPSILON_DOUBLE );
}


//----------------------------------------------------------------
// test routine checking the standard deviation of a range of 
// data values
//----------------------------------------------------------------
void UtilitiesTest::test_data_stddev()
{
  /* test 1 */
  double stddev1;
  data_stddev(dataArray1_.begin(), dataArray1_.end(), stddev1);
  CPPUNIT_ASSERT( fabs(stddev1 - sqrt(9.0729)) 
      < RELAXED_EPSILON_DOUBLE );

  /* test 2 */
  double stddev2;
  bool result = data_stddev(dataArray2_.begin(), dataArray2_.end(), 
      stddev2);
  CPPUNIT_ASSERT( result == false );

  /* test 3 */
  double stddev3;
  data_stddev(dataArray3_.begin(), dataArray3_.end(), stddev3);
  CPPUNIT_ASSERT( fabs(stddev3) < EPSILON_DOUBLE );

  /* test 4 */
  double stddev4;
  data_stddev(dataArray4_.begin(), dataArray4_.end(), stddev4);
  CPPUNIT_ASSERT( fabs(stddev4 - sqrt(1848.7)) < EPSILON_DOUBLE );
}


